# coding=utf-8
"""
作者：董新强 
创建时间：2020-5-12
描述：对外提供用于评估参数的试题
"""
import math
from operator import and_

from sqlalchemy import func

from db_models.cat_abilities import CatAbilities
from db_models.cat_questions import CatQuestions
from db_models.param_eval_answers import ParamEvalAnswers
from db_models.param_eval_test import ParamEvalTest
from framework.db_session import DbSession
from framework.log_util import trycatch
from framework.msg import failure, ok
from framework.utilities import time_str, to_int, get_len
from services.service_base import ServiceBase


class ParamEvalManage(ServiceBase):
    def __init__(self, usr):
        super().__init__(usr)

    @trycatch(failure(msg='开始答卷失败'))
    def start_test(self, aid):
        """
        开始测试
        :param aid: 能力ID
        """

        with DbSession.create() as db:
            test = db.query(ParamEvalTest.status, ParamEvalTest.qnum_part, ParamEvalTest.id, ParamEvalTest.qnums, ParamEvalTest.qid) \
                .filter(ParamEvalTest.uid == self.uid) \
                .filter(ParamEvalTest.aid == aid).first()
            if not test:
                return self._create_test(db, aid=aid)

            status, qnum_part, test_id, qnums, qid = test
            if status:
                return ok(msg='测试已完成')

            return ok(data={
                'test_id': test[2],
                'qid': qid,
                'questions': ParamEvalManage._get_question_ls(db, partion=qnum_part, qnums=qnums, aid=aid)
            })

    def _create_test(self, db, aid):
        qnums = db.query(CatAbilities.qnums).filter(CatAbilities.id == aid).first()
        if not qnums:
            return failure(msg='请选择正确的能力进行测试')

        qnums = qnums[0]
        partion_ls = [i for i in range(math.ceil(qnums / 100))]  # 试题一共分为多少块
        qnums = math.ceil(qnums / len(partion_ls))  # 每人做的题数
        partion = ParamEvalManage.select_pation(db, aid, partion_ls)
        question_ls = ParamEvalManage._get_question_ls(db, partion, qnums, aid=aid)

        # ----------------------------------------
        test = ParamEvalTest()
        test.uid = self.uid
        test.aid = aid
        test.status = 0
        test.score = 0
        test.start_time = time_str(self.now)
        test.qnum_part = partion
        test.qnums = qnums
        test.last_qid = question_ls[-1]['id']
        db.add(test)
        db.flush()
        test_id = test.id

        return ok(data={
            'test_id': test_id,
            'qid': 0,
            'questions': question_ls
        })

    @staticmethod
    def _get_question_ls(db, partion, qnums, aid):
        ls = []
        query = db.query(CatQuestions.id, CatQuestions.title, CatQuestions.options) \
            .filter(CatQuestions.aid == aid).order_by(CatQuestions.id).offset(partion * qnums).limit(qnums)
        for id, title, options in query:
            ls.append({
                'id': id,
                'title': title,
                'options': options
            })
        return ls

    @staticmethod
    def select_pation(db, aid, partions):
        min_num, min_qnum_partion, ps = 999, 999, set()

        query = db.query(func.count(ParamEvalTest.id).label('num'), ParamEvalTest.qnum_part) \
            .filter(ParamEvalTest.aid == aid) \
            .group_by(ParamEvalTest.qnum_part)
        for n, p in query:
            ps.add(p)
            if n < min_num:
                min_num, min_qnum_partion = n, p

        not_start = set(partions) - ps
        if not_start:  # 如果还有题目没有做过的,任选一个
            return list(not_start)[0]
        return min_qnum_partion

    @trycatch(failure(msg='答题失败'))
    def to_answer(self, req):
        test_id = to_int(req.get('test_id'))
        qid = to_int(req.get('qid'))
        answer = req.get('answer')
        if not test_id or not qid or get_len(answer) != 1:
            return failure(msg='参数错误')

        with DbSession.create() as db:

            test = db.query(ParamEvalTest).filter(ParamEvalTest.id == test_id).first()
            if not test or test.uid != self.uid:
                return failure(msg='尚未开始测试')
            if test.status:
                return ok(msg='测试已完成')

            # 检查问题
            question = db.query(CatQuestions.aid, CatQuestions.answer).filter(CatQuestions.id == qid).first()
            if not question or question[0] != test.aid:
                return failure(msg='参数错误')

            item = ParamEvalAnswers()
            item.test_id = test_id
            item.qid = qid
            item.answer = answer == question[1]
            item.option = answer
            db.add(item)
            test.qid = qid

            # 如果是最后一题,进行检查
            if qid == test.last_qid:
                # 检查是否真的做完了,还是修改了参数乱作
                ans = db.query(ParamEvalAnswers.qid).filter(ParamEvalAnswers.test_id == test_id).distinct().subquery()
                ans = db.query(func.count(ans.c.qid)).first()
                if ans[0] != test.qnums:
                    return failure(msg='请检查是否漏做')
                test.status = 1
                test.end_time = time_str(self.now)
                return ok(msg='测试已完成')
        return ok()

    @trycatch(failure(msg='获取能力失败'))
    def get_child_abilities(self, pid):
        ls = []
        with DbSession.create() as db:
            query = db.query(CatAbilities, ParamEvalTest.score, ParamEvalTest.status).filter(CatAbilities.pid == pid).filter(CatAbilities.status == 1) \
                .outerjoin(ParamEvalTest, and_(ParamEvalTest.aid == CatAbilities.id, ParamEvalTest.status == 1)) \
                .order_by(CatAbilities.name)
            for v, score, status in query:
                ls.append({'id': v.id, 'name': v.name, 'score': score, 'status': status})

        return ok(data=ls)

    @trycatch(failure(msg='获取能力列表失败'))
    def get_ability_list(self):
        '''
        获取能力评估使用的列表
        :return:
        '''
        ls = []
        with DbSession.create() as db:
            query = db.query(CatAbilities.id, CatAbilities.name, ParamEvalTest.status) \
                .filter(CatAbilities.pid == 0) \
                .filter(CatAbilities.status == 1) \
                .outerjoin(ParamEvalTest, and_(ParamEvalTest.uid == self.uid, ParamEvalTest.aid == CatAbilities.id)) \
                .order_by(CatAbilities.name)
            for id, name, status in query:
                ls.append({'id': id, 'name': name, 'status': 0 if not status else 1})

        return ok(data=ls)


if __name__ == '__main__':
    # qnums = 221
    # partion = math.ceil(qnums / 100)  # 试题一共分为多少块
    # print(partion)
    # times = math.ceil(qnums / partion)
    # for i in range(partion):
    #     print(times * i, times * i + times)

    aid = 3
    for i in range(5):
        for _ in range(2):
            inst = ParamEvalManage({'id': i + 1})
            result = inst.start_test(aid=aid)
            print(i + 1, '=>', result.data['questions'][-1]['id'])
